#!/usr/bin/env python
import sys
import math
import logging

import Image, ImageDraw

from canvaside.utils import safe

from canvaside.style import parseColor, formatColor

from base import BaseCanvasRenderingContext2D

class DrawContext(object):
    def __init__(self, img):
        self.img = img

    def __enter__(self):
        self.draw = ImageDraw.Draw(self.img)

        return self.draw

    def __exit__(self, exc_type, exc_value, traceback):
        del self.draw

class DrawPath(object):
    def __init__(self, ctxt):
        self.ctxt = ctxt
        self.path = Image.new("RGBA", (ctxt.width, ctxt.height), 0)
        self.draw = ImageDraw.Draw(self.path)

        self.x = self.y = 0
        self.points = []

    def moveTo(self, x, y):
        self.x = x
        self.y = y

        self.points.append((x, y,))

    def close(self):
        if len(self.points) > 1:
            self.lineTo(self.points[0][0], self.points[0][1], self.ctxt._stroke)

    def lineTo(self, x, y, color):
        self.points.append((x, y,))

        self.draw.line([(self.x, self.y), (x, y)], fill=color)

        self.x = x
        self.y = y

    def arc(self, box, start, end, color):
        self.draw.arc(box, start, end, fill=color)

    def stroke(self):
        self.ctxt._img.paste(self.path, (0, 0), self.path)

    def fill(self):
        self.close()

        self.ctxt._img.paste(self.path, (0, 0), self.path)

class CanvasRenderingContext2D(BaseCanvasRenderingContext2D):
    logger = logging.getLogger('canvaside.canvas.pil')

    def __init__(self, canvas):
        BaseCanvasRenderingContext2D.__init__(self, canvas)

        self._stroke = parseColor('black')
        self._fill = parseColor('black')

        self._img = Image.new("RGBA", (canvas.width, canvas.height), "white")
        self._path = DrawPath(self)

    @property
    def width(self):
        return self._img.size[0]

    @property
    def height(self):
        return self._img.size[1]

    def getStrokeStyle(self):
        return formatColor(self._stroke)

    def setStrokeStyle(self, value):
        self._stroke = parseColor(value)

    strokeStyle = property(getStrokeStyle, setStrokeStyle)

    def getFillStyle(self):
        return formatColor(self._fill)

    def setFillStyle(self, value):
        self._fill = parseColor(value)

    fillStyle = property(getFillStyle, setFillStyle)

    def show(self):
        self._img.show()

    def paste(self, img, x, y, w, h):
        mask = Image.new("RGBA", (w, h), 0)

        with DrawContext(mask) as draw:
            draw.rectangle([(0, 0), (w, h)], fill=self._fill)

        self._img.paste(img, (x, y), mask)

    def fillRect(self, x, y, w, h):
        """Paints the given rectangle onto the canvas, using the current fill style."""
        img = Image.new("RGBA", (w, h), 'white')

        with DrawContext(img) as draw:
            draw.rectangle([(0, 0), (w, h)], fill=self._fill)

        self.paste(img, x, y, w, h)

    def strokeRect(self, x, y, w, h):
        """Paints the box that outlines the given rectangle onto the canvas, using the current stroke style."""
        with DrawContext(self._img) as draw:
            draw.rectangle([(x, y), (x+w, y+h)], self._stroke)

    def clearRect(self, x, y, w, h):
        """Clears all pixels on the canvas in the given rectangle to transparent black."""
        with DrawContext(self._img) as draw:
            draw.rectangle([(x, y), (x+w, y+h)], fill='white')

    @safe
    def beginPath(self):
        """Resets the current path."""
        self._path = DrawPath(self)

    @safe
    def moveTo(self, x, y):
        """Creates a new subpath with the given point."""
        self._path.moveTo(x, y)

    @safe
    def closePath(self):
        """Marks the current subpath as closed, and starts a new subpath """
        """with a point the same as the start and end of the newly closed subpath."""
        self._path.close()

    @safe
    def lineTo(self, x, y):
        """Adds the given point to the current subpath, """
        """connected to the previous one by a straight line."""
        self._path.lineTo(x, y, self._stroke)

    @safe
    def arc(self, x, y, radius, startAngle, endAngle, anticlockwise):
        """Adds points to the subpath such that the arc described by """
        """the circumference of the circle described by the arguments, """
        """starting at the given start angle and ending at the given end angle, """
        """going in the given direction, is added to the path, connected to """
        """the previous point by a straight line."""
        self._path.arc((x-radius, y-radius, x+radius, y+radius),
            startAngle/math.pi*180, endAngle/math.pi*180, self._stroke)

    @safe
    def stroke(self):
        """Strokes the subpaths with the current stroke style."""
        self._path.stroke()

    @safe
    def fill(self):
        """Fills the subpaths with the current fill style."""
        self._path.fill()